package ls.repository;

import ls.model.Moaning;
import ls.model.Reply;
import ls.model.Vote;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;
import java.util.Random;
import javax.persistence.NoResultException;
import org.springframework.transaction.annotation.Propagation;

@Repository
public class JpaMoaningRepository implements MoaningRepository {
    private Random random;

    @PersistenceContext
    private EntityManager entityManager;

    public JpaMoaningRepository() {
        random = new Random(System.currentTimeMillis());
    }

    @Override
    public long getMoaningCount() {
        return entityManager.createQuery("SELECT COUNT(m) FROM Moaning m", Long.class)
                .getSingleResult();
    }

    @Override
    @Transactional
    public long saveMoaning(Moaning moaning) {
        entityManager.persist(moaning);
        return moaning.getId();
    }

    @Override
    public Moaning getMoaning(long id) {
        return entityManager.find(Moaning.class, id);
    }

    @Override
    public Moaning getRandomMoaning() {
        String query = random.nextBoolean()?
                "from Moaning m where m.random >= :mark order by m.random":
                "from Moaning m where m.random <= :mark order by m.random desc";
        long mark = random.nextLong();
        int count = 0;
        while(true) {
            try {
                return entityManager.createQuery(query, 
                        Moaning.class)
                        .setParameter("mark", mark)
                        .setFirstResult(0)
                        .setMaxResults(1)
                        .getSingleResult();
            } catch (NoResultException e) {
                if (count > 10) {
                    return entityManager.createQuery("from Moaning m", 
                            Moaning.class)
                            .setFirstResult(0)
                            .setMaxResults(1)
                            .getResultList().get(0);
                }
            }
            ++count;
        }
    }

    @Override
    public Moaning getRandomMoaning(int minReplies) {
        if (entityManager.createQuery("select count(m) from Moaning m where m.repliesCount >= :repliesCount", 
                Long.class)
                .setParameter("repliesCount", minReplies)
                .getSingleResult() == 0) {
            return null;
        }    
        String query = random.nextBoolean()?
                "from Moaning m where m.random >= :mark and m.repliesCount >= :repliesCount order by m.random":
                "from Moaning m where m.random <= :mark and m.repliesCount >= :repliesCount order by m.random desc";
        long mark = random.nextLong();
        int count = 0;
        while(true) {
            try {
                return entityManager.createQuery(query, 
                        Moaning.class)
                        .setParameter("mark", mark)
                        .setParameter("repliesCount", minReplies)
                        .setFirstResult(0)
                        .setMaxResults(1)
                        .getSingleResult();
            } catch (NoResultException e) {
                if (count > 10) {
                    return entityManager.createQuery("from Moaning m where m.repliesCount >= :repliesCount", 
                            Moaning.class)
                            .setParameter("repliesCount", minReplies)
                            .setFirstResult(0)
                            .setMaxResults(1)
                            .getResultList().get(0);
                }
            }
            ++count;
        }
    }

    @Override
    public Reply getReply(long replyId) {
        return entityManager.find(Reply.class, replyId);
    }

    @Override
    @Transactional
    public List<Moaning> getMoanings() {
        return entityManager.createQuery("from Moaning m", Moaning.class).getResultList();
    }

    @Override
    @Transactional
    public void addReply(Long moaningId, Reply reply) {
        Moaning moaning = entityManager.find(Moaning.class, moaningId);
        moaning.add(reply);
        entityManager.persist(reply);
    }

    @Override
    public void addVote(Long moaningId, Vote vote) {
        Moaning moaning = entityManager.find(Moaning.class, moaningId);
        moaning.add(vote);
        entityManager.persist(vote);
    }
}
